<?php

declare(strict_types=1);

namespace App\Controller\Admin\User;

use App\Entity\AdminUser;
use App\Entity\Notice;
use App\Entity\NoticeRead;
use App\Utils\Annotation\ApiAuthority;
use App\Utils\Annotation\ApiName;
use App\Utils\Annotation\ApiParam;
use App\Utils\Base\FilterBase;
use App\Utils\Enum\NoticeEnum;
use App\Utils\Helper\GeneralHelper;
use App\Utils\Traits\ControllerTrait;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

/**
 * Class AdminUserController
 * @package App\Controller\Admin\User
 * @Route("/admin/user/profile", name="admin-user-profile-")
 * @ApiName(name="个人中心")
 */
class ProfileController extends AbstractController {
    use ControllerTrait;

    /**
     * @ApiName(name="个人信息-更新")
     * @ApiParam(name="id", type="integer", comment="用户ID", require=true)
     * @ApiParam(name="nickname", type="string", comment="昵称")
     * @ApiParam(name="email", type="string", comment="邮箱", validation="email:true")
     * @ApiParam(name="contact", type="string", comment="联系方式-手机号", validation="phone:true")
     * @ApiParam(name="motto", type="string", comment="motto")
     * @ApiAuthority(need="admin.user.profile")
     * @Route("/update", methods={"POST"}, name="update")
     * @param Request $request
     */
    public function updateProfile (Request $request) {
        $params = $this->onlyDeclare($request);
        if ($params['id'] != $this->getUser()->getId()) {
            return json_failure('用户信息不匹配');
        }
        if (isset($params['email']) && !empty($params['email'])) {
            $admin_user_email = $this->getDoctrine()->getRepository(AdminUser::class)->findOneByFieldAndNotId('email', $params['email'], $params['id']);
            if ($admin_user_email) {
                return json_failure('邮箱"'.$params['email'].'"已经存在');
            }
        }
        if (isset($params['contact']) && !empty($params['contact'])) {
            $admin_user_contact = $this->getDoctrine()->getRepository(AdminUser::class)->findOneByFieldAndNotId('contact', $params['contact'], $params['id']);
            if ($admin_user_contact) {
                return json_failure('联系方式"'.$params['contact'].'"已经存在');
            }
        }

        $admin_user = $this->getUser();
        $admin_user->setNickname($params['nickname']);
        $admin_user->setEmail($params['email']);
        $admin_user->setContact($params['contact']);
        $admin_user->setMotto($params['motto']);
        $this->getEntityManage()->persist($admin_user);
        $this->getEntityManage()->flush();
        return json_success();
    }

    /**
     * @ApiName(name="个人密码-更新")
     * @ApiParam(name="id", type="integer", comment="用户ID", require=true)
     * @ApiParam(name="old_password", type="string", comment="旧密码", require=true)
     * @ApiParam(name="new_password", type="string", comment="新密码", validation="password:true", require=true)
     * @ApiAuthority(need="admin.user.profile")
     * @Route("/password-update", methods={"POST"}, name="password-update")
     * @param Request $request
     * @throws \Exception
     */
    public function passwordUpdate (Request $request, UserPasswordEncoderInterface $passwordEncoder) {
        $params = $this->onlyDeclare($request);
        $admin_user = $this->getUser();
        if ($params['id'] != $admin_user->getId()) {
            return json_failure('用户信息不匹配');
        }
        if (!$passwordEncoder->isPasswordValid($admin_user, $params['old_password'])) {
            return json_failure('旧密码不正确');
        }
        $admin_user->setPassword($passwordEncoder->encodePassword($admin_user, $params['new_password']));
        $this->getEntityManage()->persist($admin_user);
        $this->getEntityManage()->flush();
        return json_success();
    }

    /**
     * @ApiName(name="头像上传")
     * @ApiAuthority(need="admin.user.profile")
     * @Route("/avatar", methods={"POST"}, name="avatar")
     * @param ContainerInterface $container
     * @return \Symfony\Component\HttpFoundation\JsonResponse
     */
    public function avatar (ContainerInterface $container) {
        $admin_user = $this->getUser();
        $cos_save_path = $this->getParameter('kernel.admin_user.relative.dir').$admin_user->getId().DIRECTORY_SEPARATOR.'avatar'.DIRECTORY_SEPARATOR;
        $file = $_FILES['file'] ?? null;
        $cos_result = $container->get('app.service.tencent_cloud_cos')->uploadFileByFromFile($file, $cos_save_path);
        if ($cos_result) {
            $avatar_path = $cos_result['key'] ?? '';
            $cos_file_url = $this->getParameter('website.cdn.domain').$avatar_path;
            $admin_user->setAvatar($avatar_path);
            $this->getEntityManage()->persist($admin_user);
            $this->getEntityManage()->flush();
            $data = [
                'url' => $cos_file_url,
            ];
            return json_success($data);
        } else {
            return json_failure('头像上传失败');
        }
    }

    /**
     * @ApiName(name="获取用户通知")
     * @ApiParam(name="notice_type_id", type="integer", comment="消息通知类型ID")
     * @ApiParam(name="title", type="string", comment="标题")
     * @ApiParam(name="read_status", type="integer", comment="消息读取状态 0未读 1已读", validation="between:0,1")
     * @ApiParam(name="date", type="array", comment="日期")
     * @ApiParam(name="page", type="integer", comment="页码数", default="1")
     * @ApiParam(name="limit", type="integer", comment="每页条数", default="10")
     * @ApiAuthority(need="admin.user.profile")
     * @Route("/notice", methods={"GET"}, name="notice")
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\JsonResponse
     */
    public function getNotice (Request $request) {
        $params = $this->onlyDeclare($request);
        $read_notice_ids = $this->getDoctrine()->getRepository(NoticeRead::class)->getReadNoticesIds($this->getUser()->getId());
        // 整理查询参数
        $params['to_type'] = [
            'admin_user_group_id' => $this->getUser()->getAdminUserGroupId(),
            'admin_user_id'       => $this->getUser()->getId(),
        ];
        if (isset($params['read_status'])) {
            $params['read_status'] = [
                'value'      => $params['read_status'],
                'notice_ids' => $read_notice_ids,
            ];
        }
        $select = [
            FilterBase::ALIAS.'.id',
            FilterBase::ALIAS.'.notice_type_id',
            FilterBase::ALIAS.'.title',
            FilterBase::ALIAS.'.content',
            FilterBase::ALIAS.'.from_type',
            FilterBase::ALIAS.'.from_id',
            FilterBase::ALIAS.'.created_at',
        ];

        $notice_data = $this->getDoctrine()->getRepository(Notice::class)->search($params, $select);
        $notice_total = $this->getDoctrine()->getRepository(Notice::class)->searchTotal($params);
        // 数据整合
        foreach ($notice_data as &$notice) {
            $this->getDoctrine()->getRepository(Notice::class)->buildData($notice);
            $notice['read_status'] = 0;
            $notice['read_status_text'] = '未读';
            if (in_array($notice['id'], $read_notice_ids)) {
                $notice['read_status'] = 1;
                $notice['read_status_text'] = '已读';
            }
        }
        return json_success([
            'data'  => $notice_data,
            'total' => $notice_total,
        ]);
    }

    /**
     * @ApiName(name="获取用户通知-简版")
     * @ApiAuthority(need="admin.user.profile")
     * @Route("/simple-notice", methods={"GET"}, name="simple_notice")
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\JsonResponse
     */
    public function getSimpleNotice (Request $request) {
        $params = [
            'to_type'       => [
                'admin_user_group_id' => $this->getUser()->getAdminUserGroupId(),
                'admin_user_id'       => $this->getUser()->getId(),
            ],
            'limit'         => 5,
            'unread_notice' => [
                'admin_user_id' => $this->getUser()->getAdminUserGroupId(),
            ],
        ];
        $select = [
            FilterBase::ALIAS.'.id',
            FilterBase::ALIAS.'.title',
            FilterBase::ALIAS.'.created_at',
        ];
        // 系统消息
        $params['notice_type_id'] = NoticeEnum::NOTICE_TYPE_SYSTEM;
        $system_notice_data = $this->getDoctrine()->getRepository(Notice::class)->search($params, $select);
        $system_notice_total = $this->getDoctrine()->getRepository(Notice::class)->searchTotal($params);
        foreach ($system_notice_data as &$system_notice) {
            $system_notice['created_at'] = GeneralHelper::getInstance()->agoFilter($system_notice['created_at']);
        }
        // 审批消息
        $params['notice_type_id'] = NoticeEnum::NOTICE_TYPE_APPROVE;
        $approve_notice_data = $this->getDoctrine()->getRepository(Notice::class)->search($params, $select);
        $approve_notice_total = $this->getDoctrine()->getRepository(Notice::class)->searchTotal($params);
        foreach ($approve_notice_data as &$approve_notice) {
            $approve_notice['created_at'] = GeneralHelper::getInstance()->agoFilter($approve_notice['created_at']);
        }
        // 工单消息
        $params['notice_type_id'] = NoticeEnum::NOTICE_TYPE_WORK_ORDER;
        $work_order_notice_data = $this->getDoctrine()->getRepository(Notice::class)->search($params, $select);
        $work_order_notice_total = $this->getDoctrine()->getRepository(Notice::class)->searchTotal($params);
        foreach ($work_order_notice_data as &$work_order_notice) {
            $work_order_notice['created_at'] = GeneralHelper::getInstance()->agoFilter($work_order_notice['created_at']);
        }

        return json_success([
            'system'     => [
                'data'  => $system_notice_data,
                'total' => (int)$system_notice_total,
            ],
            'approve'    => [
                'data'  => $approve_notice_data,
                'total' => (int)$approve_notice_total,
            ],
            'work_order' => [
                'data'  => $work_order_notice_data,
                'total' => (int)$work_order_notice_total,
            ],
        ]);
    }

    /**
     * @ApiName(name="消息已读", description="已读接口始终返回json_success，不做强校验")
     * @ApiParam(name="notice_id", type="integer", comment="消息ID", require=true)
     * @ApiAuthority(need="admin.user.profile")
     * @Route("/read-notice", methods={"POST"}, name="read_notice")
     * @param Request $request
     * @param LockFactory $lock_factory
     * @return \Symfony\Component\HttpFoundation\JsonResponse
     * @throws \Exception
     */
    public function readNotice (Request $request, LockFactory $lock_factory) {
        $params = $this->onlyDeclare($request);
        $notice = $this->getDoctrine()->getRepository(Notice::class)->find($params['notice_id']);
        if (!$notice) {
            return json_failure('消息通知不存在');
        }
        $notice_read = $this->getDoctrine()->getRepository(NoticeRead::class)->findBy([
            'admin_user_id' => $this->getUser()->getId(),
            'notice_id'     => $notice->getId(),
        ]);
        if ($notice_read) {
            return json_success();
        }
        // 记录消息已读状态
        $lock_key = 'admin:user:profile:read-notice:admin-user-id:'.$this->getUser()->getId().':notice-id'.$notice->getId();
        $lock = $lock_factory->createLock($lock_key);
        if (!$lock->acquire()) {
            return json_failure('系统繁忙，稍后尝试');
        }
        try {
            $item = new NoticeRead();
            $item->setAdminUserId($this->getUser()->getId());
            $item->setNoticeId($notice->getId());
            $item->setIp($request->getClientIp());
            $this->getEntityManage()->persist($item);
            $this->getEntityManage()->flush($item);
        } catch (\Exception $e) {
            return json_failure('系统异常：'.$e->getMessage());
        } finally {
            $lock->release();
        }
        return json_success();
    }

    /**
     * @ApiName(name="全部已读", description="根据消息类型已读全部消息")
     * @ApiParam(name="notice_type_id", type="integer", validation="enum:notice_type_map", comment="消息类型ID，如果不传值则代表全部")
     * @ApiAuthority(need="admin.user.profile")
     * @Route("/read-all-notice", methods={"POST"}, name="read_all_notice")
     * @param Request $request
     */
    public function readNoticeByType (Request $request) {
        $params = $this->onlyDeclare($request);
        // 获取未读消息ID集合
        $filter_params = [
            'to_type'       => [
                'admin_user_group_id' => $this->getUser()->getAdminUserGroupId(),
                'admin_user_id'       => $this->getUser()->getId(),
            ],
            'limit'         => 100000,
            'unread_notice' => [
                'admin_user_id' => $this->getUser()->getAdminUserGroupId(),
            ],
        ];
        $select = [
            FilterBase::ALIAS.'.id',
        ];
        if (isset($params['notice_type_id'])) {
            $filter_params['notice_type_id'] = $params['notice_type_id'];
        }
        $notice_ids = $this->getDoctrine()->getRepository(Notice::class)->search($filter_params, $select);
        // 批量已读消息
        $batch_sign = 0;
        $batch_limit = 500;
        foreach ($notice_ids as $notice_id) {
            $notice_read = new NoticeRead();
            $notice_read->setNoticeId($notice_id['id']);
            $notice_read->setAdminUserId($this->getUser()->getId());
            $notice_read->setIp($request->getClientIp());
            $this->getEntityManage()->persist($notice_read);
            $batch_sign++;
            if ($batch_sign > $batch_limit) {
                $this->getEntityManage()->flush();
                $this->getEntityManage()->clear();
                $batch_sign = 0;
            }
        }
        if ($batch_sign > 0) {
            $this->getEntityManage()->flush();
            $this->getEntityManage()->clear();
        }

        return json_success();
    }
}